

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class SocketThread extends Thread {
	private static int ERRORLIMIT = 10;
	private static int BUFFERSIZE = 1024;
	
	private Socket socket;
	private DataOutputStream out;
	private BufferedReader in;
	private boolean terminate;
	private List<RequestThread> activeThread;
	private int errorLimit;
	private int clientNum;
	
	public SocketThread(Socket clientSocket, int clientNum) {
		super();
		this.socket = clientSocket;
	    this.terminate = false;
	    this.errorLimit = ERRORLIMIT;
	    this.clientNum = clientNum;
		try {
			out = new DataOutputStream(socket.getOutputStream());
		} catch (IOException e) {
			Util.pe(e, "Error when initializing the server OutputStream.", socket);
			Util.pm("Initialize new Thread error. ");
			terminate();
		}
	    try {
			in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		} catch (IOException e) {
			Util.pe(e, "Error when initializing the server InputStream.", socket);
			Util.pm("Initialize new Thread error. ");
			terminate();
		}
	    if (!this.isTerminate()) {
	    	activeThread = new ArrayList<RequestThread>();
	    }
	    Util.pl("New Client[" + clientNum + "] (" + socket.getInetAddress().getHostAddress() + ") Registered. ");
	}
	
	@Override
	public void run() {
		RequestThread requestThread = null;
		while (!terminate) {
			if (socket.isClosed() || errorLimit < 0) {
				break;
			}
			String inputLine = null;
			List<String> request = new ArrayList<String>();
			try {
				while ((inputLine = in.readLine()) != null) {
					// Check the end of the request.
					if (inputLine.equals(""))
						break;
					request.add(inputLine);
				}
				
				if (request.isEmpty()) {
					errorLimit --;
					continue;
				}
				
				requestThread = new RequestThread(request, new SocketThreadWrapper(activeThread) {
					
					@Override
					protected void failure(Exception e, String message, boolean aboutThisSocket, int requestNum) {
						if (aboutThisSocket) {
							Util.pe(e, message);
						} else {
							Util.pe(e, message, socket);
						}
						errorLimit --;
						Util.pl("Request[" + requestNum + "]: UNFINISH WITH ERROR! ");
					}

					@Override
					protected void success(DataInputStream in) {
						try {
							
							int size = BUFFERSIZE;
							while (true) {
								byte[] data = new byte[BUFFERSIZE];
								size = in.read(data);
								if (size == -1) {
									// Reach the end of the message
									break;
								}
								out.write(data, 0, size);
							}
							out.flush();
							
							
						} catch (IOException e) {
							Util.pe(e, "Error when sending data back to client. ", socket);
							errorLimit--;
							return;
						}
						errorLimit++;
					}
				}, ProxyServer.getNextRequestNum());
				activeThread.add(requestThread);
				requestThread.start();
			} catch (IOException e) {
				if (!terminate) {
					Util.pe(e, "Error when reading the inputline from user.", socket);
					terminate();
					errorLimit --;
				}
			} catch (HttpRequestFormatErrorException e) {
				if (!terminate) {
					Util.pe(e, e.getMessage());
					if (requestThread != null)
						activeThread.remove(requestThread);
					errorLimit --;
				}
			}
		}
	}
	
	public void terminate() {
		this.terminate = true;
		this.activeThread.clear();
		try {
			in.close();
			out.close();
			socket.close();
		} catch (IOException e) {
			Util.pe(e, "Error happens when closing the socket.", socket);
		}
		Util.pl("Client[" + clientNum + "] Terminated. ");
	}
	
	public boolean isTerminate() {
		return this.terminate;
	}
	
	/*
	 * Return true if there are still some RequestThread under running.
	 * 
	 */
	public boolean isUseful() {
		return !this.activeThread.isEmpty();
	}
	
	public int getClientNum() {
		return clientNum;
	}

}
